﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;


namespace ClientSocket
{
    public partial class client : Form
    {
        Socket socketClient = null;     //创建客户端套接字
        Thread threadClient = null;     //创建负责监听服务器请求的线程
        string filePath = "";           //用于存储接收的文件路径
        byte[] bytereceiveData;         //字节数组，由于存储接收文本信息     
        char[] charreceiveData;         //字符数组，用于存储接收文本信息

        public client()
        {
            //初始化
            InitializeComponent();
            //关闭对文本框的非法线程操作检查
            TextBox.CheckForIllegalCrossThreadCalls = false;
        }

        //启动服务功能键
        private void btnLoad_Click(object sender, EventArgs e)
        {
            //定义一个套子节监听包含3个参数
            socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //需要获取文本框的IP地址
            IPAddress ipaddress = IPAddress.Parse("127.0.0.1");
            txtIP.Text = "127.0.0.1";
            //将获取的Ip地址和端口号绑定到网络节点endpoint上
            IPEndPoint endpoint = new IPEndPoint(ipaddress, Convert.ToInt32(cbxPort.Text));
            //创建连接
            try
            {
                socketClient.Connect(endpoint);
                this.txtMsg.AppendText("客户端连接服务器成功！" + "\r\n");
                this.btnLoad.Enabled = false;
                //创建一个线程 用于监听服务端发来的消息
                threadClient = new Thread(RecMsg);
                //将窗体线程设置为与后台同步
                threadClient.IsBackground = true;
                //启动线程
                threadClient.Start();
            }
            catch
            {
                txtMsg.AppendText("远程服务端断开，连接失败！" + "\r\n");
            }
        }

        //接收服务端发来消息的方法
        private void RecMsg()
        {
            //持续监听服务端发送的消息
            while (true) 
            {
                try
                {
                    //定义一个内存缓冲区，用于临时性存储接收到消息
                    byte[] arrRecMsg = new byte[1024 * 1024];
                    //将客户端套接字接收到的数据存入内存缓冲区，并获取其长度
                    int length = socketClient.Receive(arrRecMsg);           
                    if (arrRecMsg[0] == 1)          //如果是1，则为文件
                    {
                        //将套接字获取到的字节数组转换为人可以看懂的字符串
                        string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);           //接收的文本信息
                        filePath = "";
                        SaveFileDialog sfd = new SaveFileDialog();
                        sfd.Title = "保存文件";
                        sfd.InitialDirectory = @"C:\Users\Administrator\Desktop";
                        sfd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
                        //如果没有选择保存文件路径就一直打开保存框
                        while (true)
                        {
                            sfd.ShowDialog(this);
                            filePath = sfd.FileName;
                            if (string.IsNullOrEmpty(filePath))
                            {     
                                
                                continue;
                            }
                            else
                            {
                                break;
                            }
                        }
                        //创建文件传输流filestream，Appdend不覆盖，create覆盖
                        FileStream file = new FileStream(filePath, FileMode.Create, FileAccess.Write);
                        charreceiveData = strRecMsg.ToCharArray();                 //将接收数据文本框的内容按字符传入charreceiveData数组
                        bytereceiveData = new byte[charreceiveData.Length];        //将charreceiveData数组的长度赋予bytereceiveData数组
                        Encoder ec = Encoding.UTF8.GetEncoder();                   //字符编码为utf8
                        ec.GetBytes(charreceiveData, 0, charreceiveData.Length, bytereceiveData, 0, true);
                        if (charreceiveData == null && bytereceiveData == null)
                        {
                            MessageBox.Show("保存失败", "error");
                        }
                        else
                        {
                            MessageBox.Show("保存成功", "true");
                        }
                        file.Write(bytereceiveData, 1, bytereceiveData.Length-1);    //写入接收信息
                        file.Flush();                                                //关闭文件传输流
                        file.Close();                                                //关闭文件
                        txtMsg.AppendText("端口号为" + cbxPort.Text + "的用户：" + GetCurrentTime() + "\r\n" + "接收文件成功！" + "\r\n");     //显示提示信息
                    }
                    else                       //否则为消息，需要解密
                    {
                        //将套接字获取到的字节数组转换为人可以看懂的字符串
                        string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
                        //将发送的消息以Base64的方式解密追加到聊天内容文本框中
                        txtMsg.AppendText("端口号为" + cbxPort.Text + "的用户：" + GetCurrentTime() + "\r\n" + Base64Decrypt(strRecMsg) + "\r\n");  //显示解密后的接收数据
                    }
                }
                catch
                {
                    txtMsg.AppendText("远程服务端已中断连接!" + "\r\n ");
                    btnLoad.Enabled = true;
                    socketClient.Close();
                }
            }
        }

        //获取当前系统时间的方法      
        private DateTime GetCurrentTime()
        {
            DateTime currentTime = new DateTime();
            currentTime = DateTime.Now;
            return currentTime;
        }

        //发送字符串消息到服务端的方法
        private void ClientSendMsg(string sendMsg)
        {
            try
            {
                //将输入的内容字符串转换为机器可以识别的字节数组
                byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);
                //调用客户端套接字发送字节数组
                socketClient.Send(arrClientSendMsg);
                //将发送的消息追加到聊天内容文本框中
                txtMsg.AppendText("端口号为" + socketClient.RemoteEndPoint.ToString() + "的用户：" + GetCurrentTime() + "\r\n" + Base64Decrypt(sendMsg) + "\r\n");
            }
            catch
            {
                txtMsg.AppendText("远程服务器已中断连接，无法发送消息！" + "\r\n");
                socketClient.Close();                   //关闭通信服务
            }
        }

        //发送功能键
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            //调用ClientSendMsg方法 将文本框中输入的消息按照Base64的方式加密发送给服务端
            ClientSendMsg(Base64Encrypt(this.txtSendMsg.Text.Trim()));
            //发送完数据后，发送文本框清空
            txtSendMsg.Clear();
        }

        //发送文件
        private void btnSendUrl_Click(object sender, EventArgs e)
        {
            try
            {
                //字符串类型变量filepath保存打开文件路径文本框的内容
                string filePath = txtUrl.Text;
                if (string.IsNullOrEmpty(filePath))
                {
                    MessageBox.Show("请选择文件");
                    return;
                }
                //读取选择的文件
                using (FileStream fsRead = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Read))
                {
                    byte[] buffer = new byte[1024 * 1024 * 2];
                    int r = fsRead.Read(buffer, 0, buffer.Length);
                    //获得发送的信息时候，在数组前面加上一个字节 1
                    List<byte> list = new List<byte>();
                    list.Add(1);
                    list.AddRange(buffer);
                    byte[] newBuffer = list.ToArray();
                    //将了标识字符的字节数组传递给客户端
                    socketClient.Send(newBuffer, 0, r + 1, SocketFlags.None);
                    //清空打开文件路径文本框
                    txtUrl.Text = "";
                }
            }
            catch
            {
            }
        }

        //打开文件路径功能键
        private void btnLoadUrl_Click(object sender, EventArgs e)
        {
            //打开文件
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Title = "选择要传的文件";
            ofd.InitialDirectory = @"C:\Users\Administrator\Desktop";
            ofd.Filter = "文本文件|*.txt|图片文件|*.jpg|视频文件|*.avi|所有文件|*.*";
            ofd.ShowDialog();
            //得到选择文件的路径
            txtUrl.Text = ofd.FileName;

        }

        //Base64加密
        public static string Base64Encrypt(string input)
        {
            return Base64Encrypt(input, new UTF8Encoding());
        }

        //调用System自带的Base64加密方法
        public static string Base64Encrypt(string input, Encoding encode)
        {
            return Convert.ToBase64String(encode.GetBytes(input));
        }
        //Base64解密
        public static string Base64Decrypt(string input)
        {
            return Base64Decrypt(input, new UTF8Encoding());
        }

        //调用System自带的Base64解密方法
        public static string Base64Decrypt(string input, Encoding encode)
        {
            return encode.GetString(Convert.FromBase64String(input));
        }
    }
}
